#ifndef MAPCLASS_H
#define MAPCLASS_H

#include "Mob.h"
#include <stdlib.h>
#include "SDL_thread.h"

struct point
{
    int posx;
    int posy;
};

struct square
{
    bool inOpenList;
    bool inCloseList;
    int cost;
    int realcost;
    int posx;
    int posy;
    square* before;
};

struct pathMessage
{
    int begposx;
    int begposy;
    int toposx;
    int toposy;
    id_ptr id;
};
class CPathFinder
{
public:
    std::list<Dir> calculatePath(int fromPosx, int fromPosy, int toPosx, int toPosy, bool inputlevel);
    MapMaster* map;
private:
    std::list<square*> openList;
    square squares[sizeHmap][sizeWmap];
    std::list<pathMessage> pathListMessage;
    void clearPathfinding();
    void addNear(int posx, int posy, int toPosx, int toPosy);
    int calcCost(int posx, int posy, int toPosx, int toPosy);
    bool removeFromOpen(int posx, int posy);
    void addToOpen(int posx, int posy);
    int numOfPathfind;
    bool level;
};



class LOSfinder
{
public:
    std::list<point>* calculateVisisble(std::list<point>* retval, int posx, int posy, bool level);
    MapMaster* map;
private:
    //bool LOSSquare[sizeHsq * 2 + 1][sizeWsq * 2 + 1];
    void clearLOS();
    std::list<point> worklist;

};

struct idpoint
{
    id_ptr id;
    int posx;
    int posy;
    int find_hash;
};

class MapMaster
{
public:
    size_t get_id() { return id;}
private:
    size_t id;  
public:
    std::vector<IMainItem*> idTable;
    MapMaster();
    int loManager;
    ASprClass aSpr;
    Manager* mobi;
    typedef std::list<id_ptr_on<IOnMapItem>> SqType;
    SqType squares[sizeHmap][sizeWmap];
    Screen* screen;
    // Sync stuff

    unsigned int hash_last;
    void Sync();
    void foreachProcess();
    void Draw();
    bool isVisible(int posx, int posy, bool level);
    
    template<typename T>
    id_ptr_on<T> newItemOnMap(unsigned int hash, int posx, int posy, size_t id_new = 0)
    {
        
        static_assert(std::tr1::is_same<IOnMapItem, T>::value || std::tr1::is_base_of<IOnMapItem, T>::value, "Error: MapMaster::newItemOnMap - type isn't derivied from IOnMapItem");
        T* item;
        item = castTo<T>(newVoidItem(hash));//TODO: FIX IT!(LOOK DOWN)
        if(item == 0)
        {
            SYSTEM_STREAM << "\nERROR! ERROR!\n";
            SDL_Delay(1000);
        }
        if(max(id_new, id) >= idTable.size())
        {
            SYSTEM_STREAM << "RESIZE MAIN TABLE\n";
            idTable.resize(max(id_new, id) * 2);
        }
        if(id_new == 0)
            id_new = id++;
        else if(id_new >= id)
        {
            SYSTEM_STREAM << id_new << " " << id << "id_ptr_on<T> newItemOn\n";
            id = id_new + 1;
        }
        item->id = id_new;
        idTable[id_new] = item;
        if(IMainItem::mob)
        {
            item->x = (posx - IMainItem::mob->posx + beginMobPosX) * TITLE_SIZE;
            item->y = (posy - IMainItem::mob->posy + beginMobPosY) * TITLE_SIZE;
            //if(IOnMapItem::isVisible(posx, posy) && IMainItem::mob->isMove)
            //    switchDir(item->x, item->y, IMainItem::mob->dMove, IMainItem::mob->way); 
        }
        else
        {
            item->x = beginMobPosX * TITLE_SIZE;
            item->y = beginMobPosY * TITLE_SIZE;
        }
        item->posx = posx;
        item->posy = posy;
//        item->isMove = 0;
        item->lastMove = 0;
//        item->way = TITLE_SIZE;

        id_ptr_on<T> retval;
        id_ptr_on<IOnMapItem> pushval;
        pushval = item->id;
        retval = item->id;
        addItemOnMap(pushval);
        return retval;
    }
    
    template<typename T, typename TMaster>
    id_ptr_on<T> newItemSaved(TMaster master, unsigned int hash, size_t id_new = 0)
    {
        T* item;
        item = castTo<T>(newVoidItemSaved(hash));
        if(max(id_new, id) >= idTable.size()) 
            idTable.resize(max(id_new, id) * 2);
        if(id_new == 0)
            id_new = id++;
        else if(id_new >= id)
            id = id_new + 1;
        idTable[id_new] = item;
        item->id = id_new;
        item->master = master;
        id_ptr_on<T> ret_val;
        ret_val = item->id;
        return ret_val;
    }
    
    template<typename T, typename TMaster>
    id_ptr_on<T> newItem(TMaster master, unsigned int hash, size_t id_new = 0)
    {
        T* item;
        item = castTo<T>(newVoidItem(hash));
        if(max(id_new, id) >= idTable.size()) 
            idTable.resize(max(id_new, id) * 2);
        if(id_new == 0)
            id_new = id++;
        else if(id_new >= id)
            id = id_new + 1;
        idTable[id_new] = item;
        item->id = id_new;
        item->master = master;
        id_ptr_on<T> ret_val;
        ret_val = item->id;
        return ret_val;
    }

    template<typename T>
    SqType::iterator getItem(int posx, int posy)
    {
        SqType::iterator retval;
        for(retval = squares[posx][posy].begin(); retval != squares[posx][posy].end(); ++retval)
        {
            if(castTo<T>(**retval) != nullptr)
                break;
        }
        return retval;
    }

    template<typename T>
    SqType::iterator getItemOnly(int posx, int posy)
    {
        SqType::iterator retval;
        for(retval = squares[posx][posy].begin(); retval != squares[posx][posy].end(); ++retval)
        {
            if((*retval)->RT_ITEM() == T::RT_ITEM_S())
                break;
        }
        return retval;
    }
    
    void splashLiquid(std::list<HashAmount> ha, int posx, int posy);

    static IMainItem* newVoidItem(unsigned int type);
    static IMainItem* newVoidItemSaved(unsigned int type);

    unsigned int hash_all();

    void makeMap();
    void addItemOnMap(id_ptr_on<IOnMapItem> pushval);
    void saveMap(const char* path);
    void saveMap(std::stringstream& str);//to strstream
    void loadMap(const char* path);
    void loadMap(std::stringstream& str);
    void loadMapFromNet();
    void clearMap();
    void centerFromTo(int beginPosx, int beginPosy, int nowPosx, int nowPosy);
    bool isPassable(int posx, int posy, bool level);
    bool fastisPassable(int posx, int posy);
    static void switchDir(int& posx, int& posy, Dir direct, int num = 1, bool back = false);
    static bool checkOutBorder(int posx, int posy);
    //
    static bool checkOutBorder(int posx, int posy, Dir direct);
    //pathfinding

    size_t ms_last_draw;
    bool canDraw();
    
    id_ptr_on<IOnMapItem> click(int x, int y);
    
    int numOfPathfind;
    CPathFinder pathf;
    LOSfinder losf;
    void checkZeroId();
};

#endif